React学习

2018.10.18 星期四 9:46

不是MVC,只是View;需要配合使用,router,flux/redux等
单向数据流,不是双向数据绑定,
视图更新(响应式):通过setState触发生命周期,进行diff算法,更新
css/样式文件, 行内css/css对象:伪类/动画困难; 库:css modules,style component,动画支持…
UI:ant Design(antd)

基于: v16.5

React 顶层 API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 16.5 react.development.js
var React = {
Children: { // ### 3 转换元素
map: mapChildren, // 如果children是一个Fragment,它将被视为一个子组件并且不会被遍历。
forEach: forEachChildren,
count: countChildren,
toArray: toArray,
only: onlyChild
},

// ### 1 组件(Components)
Component: Component,
PureComponent: PureComponent,

createContext: createContext,
// ### 5 Refs
createRef: createRef,
forwardRef: forwardRef,
// ### 4 片段(Fragments)
Fragment: REACT_FRAGMENT_TYPE,

StrictMode: REACT_STRICT_MODE_TYPE,
unstable_AsyncMode: REACT_ASYNC_MODE_TYPE,
unstable_Profiler: REACT_PROFILER_TYPE,

// ### 2 创建 React 元素
createElement: createElementWithValidation,
createFactory: createFactoryWithValidation,
// ### 3 转换元素 ,还有上面的Children
cloneElement: cloneElementWithValidation,
isValidElement: isValidElement,

version: ReactVersion,

__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactSharedInternals
};
// ## 2 参考
React. createElement/cloneElement( type,[props],[...children])
React.createFactory(type)
React.isValidElement(object)

forwardRef

forwardRef: https://zh-hans.reactjs.org/docs/react-api.html#reactforwardref

React.forwardRef 接受渲染函数作为参数。React 将使用 props 和 ref 作为参数来调用此函数。此函数应返回 React 节点。

React.forwardRef 会创建一个React组件,这个组件能够将其接受的 ref 属性转发到其组件树下的另一个组件中。这种技术并不常见,但在以下两种场景中特别有用:

  • 转发 refs 到 DOM 组件
  • 在高阶组件中转发 refs

JSX

每一个 JSX 元素都是调用 React.createElement(component, props, …children) 的语法糖,

1
2
3
4
5
6
7
8
9
ReactDOM.render(
<Hello toWhat="World" />,
document.getElementById('root')
);
// 编译成不使用JSX的代码:
ReactDOM.render(
React.createElement(Hello, {toWhat: 'World'}, null),
document.getElementById('root')
);

ES6/ES5

### 自定义组件
### props
### 设置初始化 State(状态)
### 事件/绑定this:$PS始终指向定义时的对象
如果你想更稳妥的方法,你有以下选择:
4.1 在构造函数中绑定方法。
4.2 使用箭头函数,例如,onClick={(e) => this.handleClick(e)}。
4.0 保持使用 createReactClass。
4.3 使用箭头绑定方法 语法是实验性的,并且这个语法将来可能会发生变化,或者这个提案可能不会纳入语言范畴。

4.4 bind(this),箭头函数也是绑定了this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
class Greeting extends React.Component {
// ### 设置初始化 State(状态)
constructor(props) {
super(props);
this.state = {count: props.initialCount};
// #### 4.1 这一行很重要!
this.handleClick = this.handleClick.bind(this);
}
handleClick() { // #### 4.1 / 4.2
alert(this.state.message);
}
// #### 4.3 这里使用箭头绑定方法. 警告:这个语法是实验性的!
handleClick = () => {
alert(this.state.message);
}

render() {
// return <h1>Hello, {this.props.name}</h1>; //
// 因为 `this.handleClick` 是绑定的,所以我们可以使用它作为一个事件处理程序。
return (

<button
// #### 4.1 /4.3
onClick={this.handleClick}
// #### 4.2
onClick={(e) => this.handleClick(e)}
// #### 4.4 bind 和 4.2 箭头函数 相同
onChange={function(){this.changTextValue() }.bind(this)}
>
Say hello
</button>
);
}
}
Greeting.defaultProps = {
name: 'Mary'
};
// ## ES5
// ### 1
var createReactClass = require('create-react-class');
var Greeting = createReactClass({
// ### 2
getDefaultProps: function() {
return {
name: 'Mary'
};
},
// ### 3
getInitialState: function() {
return {count: this.props.initialCount};
},
// ### 4.0 在 createReactClass() 中,并不需要这么做,因为方法可以自动绑定。
handleClick: function() {
alert(this.state.message);
},
render: function() {
// return <h1>Hello, {this.props.name}</h1>;
return (
<button onClick={this.handleClick}>
Say hello
</button>
);
}
});

组件,模块和类

函数式组件和类组件:如果你需要使用 ref ,你需要将组件转化成 类(class)组件,就像需要 生命周期方法 或者 state 一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ### 1 函数式组件和类组件      
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
// ### 2 代表 DOM 标签的 React 元素:和 用户定义的组件
const element = <div />;
const element = <Welcome name="Sara" />;

export default Welcome;

非受控组件 VS controlled component

单纯/不受控制的话,使用非受控组件很容易;
受控的话,不控制shouldUpdate,会一直更新;还需要做逻辑/设置onChange

高阶组件 VS Mixin

React 最早也是使用 mixins 的,不过后来他们觉得这种方式对组件侵入太强会导致很多问题,就弃用了 mixinx 转而使用 HoC,

模块

使用ES6 import/export
不用ES5 commomjs: require / module.exports
$BLOB:js模块规范

属性(props) 和 状态(state)

类属性(Class Properties):defaultProps

defaultProps 可以定义为组件类自身的属性,用来设置类的默认 props 。 这用于未定义的(undefined) props,但不用于 null props 。

还有一个类属性:displayname:为调试目的显示不同的名称或者创建高阶组件时
$PS: 静态属性defaultProps,对应静态方法static getDefaultProps(): 在类定义时使用
$PS:另外的静态属性: propTypes; 详见 [## 静态类型检查]

#### 实例获取类的静态属性(defaultProps可以被实例访问)

在函数和 ES6 classes(类) 中,defaultProps 被定义为组件本身的属性

上面的描述没有在ES6 中找到,react中实例可以获取类的静态属性可以用下面的解释:
正常情况下,类的静态属性,实例是无法直接获取的;但是在react中,静态方法getDeafultProps中super作为对象,指向父类非原型,所以实例可以访问类的静态属性了.
$PS_疑问:但是,在v16.5.2文档中没有找到这个静态方法啊

super这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。
第一种情况,super作为函数调用时,代表父类的构造函数。
第二种情况,super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

#### 用法
由于是用ES6 class语法创建组件,其内部只允许定义方法,而不能定义属性,class的属性只能定义在class之外。所以defaultProps要写在组件外部。

组件内部:如果babel设置为es6的转码方式,会报错,因为定义静态属性不属于es6,而在es7的草案中。ES6的class中只有静态方法,没有静态属性。需要设置babel或者webpack(添加query-> presets-> stage-0)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Hello extends Component{
/* static getDefaultProps(){
return {}
} */
// ## ES7
static defaultProps={
name: 'XiaoHong'
}
static propTypes={
name: PropTypes.string,
sex: PropTypes.string.isRequired
}
}
// ## ES6 或者分离出来
Hello.defaultProps={name:'xiaohong'}
Hello.propsTypes={}

实例属性(Instance Properties): props

this.props 包含此组件的调用者定义的 props 。

Props 是只读的
论你用函数或类的方法来声明组件, 它都无法修改其自身 props.

所有 React 组件都必须是纯函数,并禁止修改其自身 props 。

当然, 应用 UI 总是动态的,并且随时有可以改变。 所以在下一节, 我们会介绍一个新的概念 state(状态) 。state 允许 React 组件在不违反上述规则的情况下, 根据用户操作, 网络响应, 或者其他随便什么东西, 来动态地改变其输出。

实例属性(Instance Properties): state

state(状态) 包含该组件的的特定数据,该数据可能随时间而变化。 状态是用户定义的,它应该是一个纯粹的 JavaScript 对象。
如果你不在 render() 中使用它,它就不应该是 state 。 例如,您可以直接在实例上放置定时器ID。

永远不要直接改变 this.state ,因为调用 setState() 之后可能会覆盖你所做的这个改变。 把 this.state 看作是不可变的。

#### 正确地使用 State(状态)
1) 不要直接修改 state(状态)
2) state(状态) 更新可能是异步的
React 为了优化性能,有可能会将多个 setState() 调用合并为一次更新。
因为 this.props 和 this.state 可能是异步更新的,你不能依赖他们的值计算下一个state(状态)。
3) state(状态)更新会被合并
当你调用 setState(), React 将合并你提供的对象到当前的状态中。
例如,你的状态可能包含几个独立的变量:然后通过调用独立的 setState() 调用分别更新它们:
合并是浅合并,所以 this.setState({comments}) 不会改变 this.state.posts 的值,但会完全替换this.state.comments 的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// ### 1
this.state.comment = 'Hello'; // 错误 这样将不会重新渲染一个组件:
// ### 2
// 错误 可能导致 counter(计数器)更新失败:
this.setState({
counter: this.state.counter + this.props.increment,
});
// 应该使用第 2 种 setState() 的格式,它接收一个函数,而不是一个对象。
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
// ### 3
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});

fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}

事件

事件处理: https://react.docschina.org/docs/handling-events.html
合成事件:https://react.docschina.org/docs/events.html

SyntheticEvent 实例将被传递给你的事件处理函数,它是浏览器的原生事件的跨浏览器包装器。除兼容所有浏览器外,它还拥有和浏览器原生事件相同的接口,包括 stopPropagation() 和 preventDefault()。
如果因为某些原因,当你需要使用浏览器的底层事件时,只需要使用 nativeEvent 属性来获取即可。
SyntheticEvent 对象都包含以下属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
boolean bubbles
boolean cancelable
DOMEventTarget currentTarget
boolean defaultPrevented
number eventPhase
boolean isTrusted
DOMEvent nativeEvent
void preventDefault()
boolean isDefaultPrevented()
void stopPropagation()
boolean isPropagationStopped()
void persist() // > 从 v17 开始,e.persist() 将不再生效,因为 SyntheticEvent 不再放入事件池中。
DOMEventTarget target
number timeStamp
string type

事件机制原理

react 的所有事件并没有绑定到具体的dom节点上而是绑定在了document 上,然后由统一的事件处理程序来处理,同时也是基于浏览器的事件机制(冒泡),所有节点的事件都会在 document 上触发。
<!–

原生事件混用

浏览器事件的执行需要经过三个阶段,捕获阶段-目标元素阶段-冒泡阶段。

  • 原生事件(阻止冒泡)会阻止合成事件的执行
  • 合成事件(阻止冒泡)不会阻止原生事件的执行
    两者最好不要混合使用,避免出现一些奇怪的问题
    –>
    #### 意义
    减少内存消耗,提升性能,不需要注册那么多的事件了,一种事件类型只在 document 上注册一次
    统一规范,解决 ie 事件兼容问题,简化事件逻辑
    对开发者友好
    #### 事件的合成和处理
    对原生事件的封装
    对某些原生事件的升级和改造
    不同浏览器事件兼容的处理

##### 对原生事件的封装
click事件的回调方法,方法中的参数 e,其实不是原生事件对象而是react包装过的对象,同时原生事件对象被放在了属性 e.nativeEvent内。

SyntheticEvent是react合成事件的基类,定义了合成事件的基础公共属性和方法。
##### 对原生事件的升级和改造

对于有些dom元素事件,我们进行事件绑定之后,react并不是只处理你声明的事件类型,还会额外的增加一些其他的事件,帮助我们提升交互的体验。
<!–
这里就举一个例子来说明下:
当我们给input声明个onChange事件,看下 react帮我们做了什么?
可以看到react不只是注册了一个onchange事件,还注册了很多其他事件。

而这个时候我们向文本框输入内容的时候,是可以实时的得到内容的。

然而原生只注册一个onchange的话,需要在失去焦点的时候才能触发这个事件,所以这个原生事件的缺陷react也帮我们弥补了。 –>
##### 浏览器事件的兼容处理

#### 事件注册机制
react 事件注册过程其实主要做了2件事:事件注册、事件存储。

a. 事件注册 - 组件挂载阶段,根据组件内的声明的事件类型-onclick,onchange 等,给 document 上添加事件 -addEventListener,并指定统一的事件处理程序 dispatchEvent。
b. 事件存储 - 就是把 react 组件内的所有事件统一的存放到一个对象里,缓存起来,为了在触发事件的时候可以查找到对应的方法去执行。

##### 源码解析

#### 事件执行机制
在事件注册阶段,最终所有的事件和事件类型都会保存到 listenerBank中。
事件触发过程总结为主要下面几个步骤:

1.进入统一的事件分发函数(dispatchEvent)
2.结合原生事件找到当前节点对应的ReactDOMComponent对象
3.开始 事件的合成
3.1 根据当前事件类型生成指定的合成对象
3.2 封装原生事件和冒泡机制
3.3 查找当前元素以及他所有父级
3.4 在 listenerBank查找事件回调并合成到 event(合成事件结束)
4.批量处理合成事件内的回调事件(事件触发完成 end)

生命周期

react生命周期: http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

Mounting(装载)

当组件实例被创建并将其插入 DOM 时,将按以下顺序调用这些方法:

constructor(props)
static getDerivedStateFromProps(props, state)
render()
componentDidMount()

UNSAFE_componentWillMount()

Updating(更新)

更新可以由对 props 或 state 的更改引起。当重新渲染组件时,按以下顺序调用这些方法:
static getDerivedStateFromProps(props, state)
shouldComponentUpdate(nextProps, nextState)
render()
getSnapshotBeforeUpdate(prevProps, prevState)
componentDidUpdate(prevProps, prevState, snapshot)

UNSAFE_componentWillUpdate()
UNSAFE_componentWillReceiveProps()

Unmounting(卸载)

componentWillUnmount()

错误处理

在生命周期方法中,或在任何子组件的构造函数中,渲染过程中出现错误时调用此方法。
componentDidCatch(error, info)

#### render
render() 方法是类组件中唯一必须的方法。
当被调用时,它会检查 this.props 和 this.state 并返回其中一个类型:React元素,数组和片段(fragments),Portals,字符串和数字,布尔值 或 null

render() 函数应该是纯函数,这意味着它不会修改组件状态,每次调用它时返回相同的结果,它不会直接与浏览器交互。
如果您需要与浏览器交互,请改用 componentDidMount() 或其他生命周期方法执行你的工作。

如果 shouldComponentUpdate() 方法返回 false ,render() 不会被调用。

#### constructor
不应该在 constructor() 中调用 setState()。相反,如果您的组件需要使用本地 state,直接在构造函数中 将初始状态赋给 this.state 即可

避免复制 属性(props) 到 状态(state) !.仅在你故意要忽略 属性(props)更新时,使用此模式。

#### static getDerivedStateFromProps(props, state)
这种方法适用于罕见用例 ,其 state(状态) 取决于 属性(props) 随着时间的推移而改变。 例如,实现一个 组件可能会比较方便,该组件比较其前一个和下一个子 state (状态),以决定哪些子元素可以进入和退出。

单向数据流

refs和转发refs

静态类型检查

对于更大的代码库我们建议使用 Flow 或者 TypeScript 来替代 PropTypes。
原因: 像 Flow 和 TypeScript 这样的静态类型检查器可以在运行代码之前识别某些类型的问题。 他们还可以通过添加自动完成功能来改善开发人员的工作流程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import PropTypes from 'prop-types';
App.propTypes={
name:PropTypes.string.isRequired,
todos: PropTypes.arrayOf(PropTypes.object).isRequired,
add:PropTypes.func.isRequired
}
App.defaultProps={
name:'anonymous',
todos:[],
add:()=>{}
}
// $PS:写在类声明中
class App extends Component{
static getDefaultProps(){
return {
name: ''
}
}
// $PS 新的是属性
static defaultProps={}

static propTypes={
name:PropTypes.string.isRequired
}
}

create-react-app

\public\ 文件夹为web服务器,放置数据文件,比如json,可以通过fetch/ajax 异步获取数据

React.render()和reconciliation

setState(updater, [callback])

基本说明

1) setState() 排队更改组件的 state ,并通过更新 state 来告诉 React ,该组件及其子组件需要重新渲染。这是用于 响应事件处理程序 和 服务器响应 更新用户界面的主要方法。

2) 记住 setState() 作为一个请求,而不是立即命令来更新组件。为了更好的感知性能,React 可能会延迟它,然后合并多个setState()更新多个组件。 React不保证 state 更新就立即应用(重新渲染)。

3) setState() 并不总是立即更新组件。它可能会 批量 或 延迟到后面更新。这使得在调用 setState() 之后立即读取 this.state 存在一个潜在的陷阱。 而使用 componentDidUpdate 或 setState 回调(setState(updater, callback)),在应用更新后,都将被保证触发。如果你需要根据先前的 state 设置 state,阅读下面的 updater 参数。

4) setState() 总是会导致重新渲染,除非 shouldComponentUpdate() 返回 false 。如果可变对象被使用,并且条件渲染逻辑不能在 shouldComponentUpdate() 中实现,只有当新 state 与先前 state 不同时调用 setState() 才能避免不必要的重新渲染。

### 1 updater 函数
在这个签名中,第一个参数是的一个 updater 函数:
state 是对先前 state 的引用。 它不会直接突变。 相反,应该根据输入的 state 和 props 构建一个新的对象来表示更改。

### 2 一个对象
您可以随意的传递 一个对象 作为 setState() 的第一个参数,而不是一个函数:
这将执行 stateChange 的浅合并到新的 state
这种形式的 setState() 也是异步的,并且在同一周期内的多个调用可以被合并在一起执行批处理。

同一周期中,后续调用将覆盖先前调用的值,所以数量只会增加一次。如果下一个 state 取决于当前的 state ,我们推荐使用 updater 函数形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// ### 1 updater 函数 
this.setState((state, props) => { // state:prevState
return {counter: state.counter + props.step};
});
// ### 2 一个对象
this.setState({quantity: 2})
// 例如,如果您尝试在同一周期内多次增加项目数量,这将导致的结果相当于:
Object.assign(
previousState,
{quantity: state.quantity + 1},
{quantity: state.quantity + 1},
...
)
// 正确updater函数
this.setState((state) => {
return {counter: state.quantity + 1};
});

// ### $PS: 一个对象 其他用法
this.setState({todos:_add(index,value)})
function _add(index.value){
// do sth
retrun {} // 返回一个对象
}

理解合并多个

将多个 setState() 调用合并为一次更新。以后一个为准

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ### 1.1 stateChange
this.setState({count:this.state.count+index})
this.setState({count:this.state.count+10})

// ### 1.2 和上面一样,虽然是updater,但是仍然用了this.state.count; 没有用 state参数
this.setState((state,props)=>({count:this.state.count+index}))
this.setState((state,props)=>({count:this.state.count+10}))

// ### 3 正确的updater,会增加两次
this.setState((preState,props)=>{
return {count:preState.count+index}
})
this.setState((preState,props)=>{
return {count:preState.count+index}
})

updater return问题

1) 如果没有返回值return, 不会render,不论todos有没有改变
2) 有return, todos没有变化,也不会render
3) todos有变化,return {} / {others:’haha’} /{others:[]}/… 正常
4) todos有变化,return {todos:[]}, 根据返回的todos做更新:如果[]就为0,如果一个就是一个

原理分析

setState被调用之后,更新组件的过程,下面是一个简单的流程图。

1) setState = function (partialState, callback) : 这里的partialState可以传object,也可以传function,它会产生新的state以一种Object.assgine()的方式跟旧的state进行合并。
2) enqueueSetState: 1、将新的state放进数组里 2、用enqueueUpdate来处理将要更新的实例对象
3) enqueueUpdate: 当前如果正处于创建/更新组件的过程,就不会立刻去更新组件,而是先把当前的组件放在dirtyComponent里,所以不是每一次的setState都会更新组件~。

这段代码就解释了我们常常听说的:setState是一个异步的过程,它会集齐一批需要更新的组件然后一起更新。
4) batchingStrategy: 1、如果当前事务正在更新过程中,则使用enqueueUpdate将当前组件放在dirtyComponent里。 2、如果当前不在更新过程的话,则执行更新事务。
5) transaction:

1
2
3
4
5
6
7
8
// ReactBaseClassses.js
ReactComponent.prototype.setState = function (partialState, callback) {
// 将setState事务放进队列中
this.updater.enqueueSetState(this, partialState);
if (callback) {
this.updater.enqueueCallback(this, callback, 'setState');
}
};

Immutable data 和 setstate

$PS_原因:因为 state 跟 props 如果没变的话,本来就不该触发 render function。
所以在用setState的时候总是要产生一个新的物件,而不是直接对现有的做操作。

使用PureComponent ,不是自己编写 shouldComponentUpdate,提升性能
避免(PureComponent对象/数组是引用不会进行更新)最简单的方法是不要突变(mutate) props 或 state 的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// ### 1.1 不能这样  
constnewObject = this.state.obj
newObject.id = 2;
this.setState({obj: newObject})
// ### 1.2 也不能这样
constarr = this.state.arr;
arr.push(123);
this.setState({list: arr})
// $PS1: 如果是PureComponent,仅会进项浅比较,不会更新. Component会更新
// $PS2:通过赋值变量更改不会提示: Do not mutate state directly. Use setState()
// 直接操作:this.setState({list:this.statelist.push(123)}) // 会提示: Do not mutate state directly. Use setState()
// 个人高级没有差别
// $PS3: push/splice, += 不会提示 Do not mutate state directly. Use setState()

// ## 2 正确用法
handleClick() {
this.setState(state => ({
// ### 2.1 concat 重写
words: state.words.concat(['marklar'])
// ### 2.2 ES6 对于数组支持展开语法
words: [...state.words, 'marklar'],
// ### 2.3 Object.assign // 属于ES6语法,需要 polyfill。
words: Object.assign({}, colormap, {right: 'blue'});
// ### 2.4 JavaScript提案添加了对象展开符 ,能够更简单地更新对象而不突变对象。
words: {...colormap, right: 'blue'};
}));
}

使用 Immutable 数据结构

Immutable.js 是解决上述问题的另外一个方法,其提供了通过结构共享实现(Structural Sharing)地不可变的(Immutable)、持久的(Persistent)集合
其他两个:seamless-immutable 和 immutability-helper

提供了一种更简单的方式来追踪对象的改变,这正是我们实现 shouldComponentUpdate 所需要的。这将会提供可观的性能提升。

1
2
3
4
5
6
7
8
9
10
11
12
// ## 0 下面是普通的JavaScript代码:
const x = { foo: 'bar' };
const y = x;
y.foo = 'baz';
x === y; // true
// ## 1 immutable.js
const SomeRecord = Immutable.Record({ foo: null });
const x = new SomeRecord({ foo: 'bar' });
const y = x.set('foo', 'baz');
const z = x.set('foo', 'bar');
x === y; // false
x === z; // true

19:33

ReactDOM.render(element, container[, callback])

渲染一个 React 元素到由 container 提供的 DOM 中,并且返回组件的一个 引用(reference) (或者对于 无状态组件 返回 null )。

如果 React 元素先前已经被渲染到了 container 中,那么将对其执行更新,并且对 DOM 只修改需要修改的地方,以反映最新的 React元素。

如果提供了可选的回调,它将在组件渲染或更新后执行。

ReactDOM.render() 当前返回根 ReactComponent 实例的引用。但是,使用此返回值是历史遗留的,应该避免使用,因为在某些情况下,React 的未来版本可能会异步渲染组件。如果您需要引用根 ReactComponent 实例,优选的解决方案是绑定一个 ref 回调 到根元素。

### 用处
返回的根实例的引用,可以在全局调用 根实例 的属性/方法,比如state/props/refs,生命周期方法, change/toggle等. 这样就可以通过外部方式调用react实例方法 异步 向react 组件传递数据,比如外部jquery的ajax,CEF传递的数据.
同步数据,react组件内部可以直接使用

回调函数callback其实是没有参数的,但是,当render方法变成异步方法之后,说不定就会向其注入一些参数了。具体的,拭目以待。

#### 第二种方式
在componentDidMount中,可以把实例暴露出去,比如暴露到 windows 上

1
2
3
4
5
6
7
componentDidMount(){
windows.__yourReactComp__ = this
}
// somewhere
if(windows.__yourReactComp__.state.bar){
console.log("you got it!")
}

其他

遍历对象的时候用for报错:”*不能遍历的属性” ,估计是 Symbol
用了Object.keys(obj) (ES2017 引入了跟Object.keys配套的Object.values Object.entries) 遍历数组(obj对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。)

Todolist

1) 所有input(add和change)使用了非受控组件defalutValue,只有onBlur/Enter的时候,才会触发更新;受控组件的话每次onChange都需要做更新
2) 切换isEdit中,TodoInput 组件input需要设置 autoFocus,否则点击其他事项的时候,上一个不会做切换

Todo

高阶组件,转发refs,

knowledge is no pay,reward is kindness
0%